#include "baseModel.h"
#include "m3dLoader.h"
#include "textureMgr.h"
#include "mathHelper.h"

MeshGeometry::MeshGeometry()
{
	mIndexBufferFormat = DXGI_FORMAT_R32_UINT;

}

MeshGeometry::~MeshGeometry()
{
	if (mIB)
	{
		mIB->Release();
	}
	if (mVB)
	{
		mVB->Release();
	}
}

template<typename VertexType>
void MeshGeometry::SetVertices(ID3D11Device* device, const VertexType* vertices, UINT count)
{
	mVertexStride = sizeof(VertexType);

	D3D11_BUFFER_DESC bd;
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = mVertexStride * count;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	D3D11_SUBRESOURCE_DATA InitData;
	ZeroMemory(&InitData, sizeof(InitData));
	InitData.pSysMem = vertices;
	HRESULT hr = device->CreateBuffer(&bd, &InitData, &mVB);
	if (FAILED(hr))
		return ;
}

void MeshGeometry::SetIndices(ID3D11Device* device, const UINT* indices, UINT count)
{
	mIndexBufferFormat = DXGI_FORMAT_R32_UINT;

	D3D11_BUFFER_DESC bd;
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof(UINT) * count;
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	D3D11_SUBRESOURCE_DATA InitData;
	ZeroMemory(&InitData, sizeof(InitData));
	InitData.pSysMem = indices;
	HRESULT hr = device->CreateBuffer(&bd, &InitData, &mIB);
	if (FAILED(hr))
		return;
}

void MeshGeometry::SetSubsetTable(std::vector<Subset>& subsetTable)
{
	mSubsetTable.resize(subsetTable.size());
	copy(subsetTable.begin(), subsetTable.end(), mSubsetTable.begin());
}

void MeshGeometry::Draw(ID3D11DeviceContext* dc, UINT subsetId)
{
	UINT offset = 0;
	dc->IASetVertexBuffers(0, 1, &mVB, &mVertexStride, &offset);
	dc->IASetIndexBuffer(mIB, mIndexBufferFormat, 0);
	dc->DrawIndexed(
		mSubsetTable[subsetId].FaceCount * 3,
		mSubsetTable[subsetId].FaceStart * 3,
		0);
}

//==================================================================
//class basemodel

BaseModel::BaseModel(ID3D11Device* device, TextureMgr& texMgr, const std::string& modelFilename, const std::wstring& texturePath)
{
	std::vector<M3dMaterial> mats;
	CM3dLoaer m3dLoader;
	m3dLoader.LoadM3d(modelFilename, Vertices, Indices, Subsets, mats, mSkinnedData);
	ModelMesh.SetVertices(device, &Vertices[0], Vertices.size());
	ModelMesh.SetIndices(device, &Indices[0], Indices.size());
	ModelMesh.SetSubsetTable(Subsets);

	auto piter = mSkinnedData.mAnimations.begin();
	if (piter != mSkinnedData.mAnimations.end())
	{
		mClipName = piter->first;
	}
	
	SubsetCount = mats.size();
	for (UINT i = 0; i < SubsetCount; ++i)
	{
		Mats.push_back(mats[i].Mat);

		ID3D11ShaderResourceView* diffuseMapSRV = texMgr.CreateTexture(device, 
			texturePath + mats[i].DiffuseMapName);
		DiffuseMapSRV.push_back(diffuseMapSRV);
		ID3D11ShaderResourceView * normalMapSRV = texMgr.CreateTexture(device,
			texturePath + mats[i].NormalMapName);
		NormalMapSRV.push_back(normalMapSRV);
	}

	XMStoreFloat4x4(&mWorld, XMMatrixIdentity());
	XMStoreFloat4x4(&mWorldInvTrans, InverseTranspose(XMLoadFloat4x4(&mWorld)));

	//create constant buffer
	D3D11_BUFFER_DESC bd;
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof(CBPerObject);
	bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	HRESULT hr = device->CreateBuffer(&bd, NULL, &mCBPerObj);
	if (FAILED(hr))
	{
		return;
	}

	//
	bd.ByteWidth = sizeof(CBBoneTransforms);
	hr = device->CreateBuffer(&bd, nullptr, &mCBBoneTransforms);
	if (FAILED(hr))
	{
		return;
	}
}

BaseModel::~BaseModel()
{
	
}

void BaseModel::Draw(ID3D11DeviceContext* dc)
{
	CBPerObject cbPerObj;
	cbPerObj.mWorld = XMMatrixTranspose(XMLoadFloat4x4(&mWorld));
	cbPerObj.mWorldInvTrans = XMMatrixTranspose(XMLoadFloat4x4(&mWorldInvTrans));

	CBBoneTransforms cbBone;
	copy(mBoneTransforms.begin(), mBoneTransforms.end(), cbBone.mBoneTransforms);
	dc->UpdateSubresource(mCBBoneTransforms, 0, nullptr, &cbBone, 0, 0);
	dc->VSSetConstantBuffers(3, 1, &mCBBoneTransforms);

	for (UINT i = 0; i < SubsetCount; ++i)
	{
		cbPerObj.mMat = Mats[i];
		dc->UpdateSubresource(mCBPerObj, 0, NULL, &cbPerObj, 0, 0);
		dc->VSSetConstantBuffers(2, 1, &mCBPerObj);
		dc->PSSetConstantBuffers(2, 1, &mCBPerObj);
		dc->PSSetShaderResources(0, 1, &DiffuseMapSRV[i]);
		dc->PSSetShaderResources(1, 1, &NormalMapSRV[i]);
		ModelMesh.Draw(dc, i);
	}
}

void BaseModel::UpdateWorldMatrix(const XMFLOAT4X4& M)
{
	mWorld = M;
	XMStoreFloat4x4(&mWorldInvTrans, InverseTranspose(XMLoadFloat4x4(&mWorld)));
}

void BaseModel::Update(float timePos)
{
	mBoneTransforms.resize(0);
	std::vector<XMFLOAT4X4> boneTransforms;
	mSkinnedData.GetFinalTransforms(mClipName, timePos, boneTransforms);
	for (UINT i = 0; i < boneTransforms.size(); ++i)
	{
		mBoneTransforms.push_back(XMMatrixTranspose(XMLoadFloat4x4(&boneTransforms[i])));
	}
}